package dao

import (
	"gitee.com/wzpwzy/gogii/conf"
	"gitee.com/wzpwzy/gogii/tools"
	"strings"
)

/*
*创建Es curd实现方法
*
 */

func GenerateEs(op conf.Options) {
	op.Name = strings.Title(op.Name)
	content := `package `
	if op.Namespace == "" {
		content += `elastic`
	} else {
		content += op.Namespace
	}

	if op.ModelsNamespace == "" {
		op.ModelsNamespace = "models"
	}
	index := tools.Camel2Case(op.Name)
	content += `
import (
 	"context"
	"fmt"
	"gitee.com/wzpwzy/gogii/client"
	"github.com/olivere/elastic/v7"
	model "` + op.ModelsNamespace + `"
	"reflect"
	"strconv"
)
	
const (
	esRetryLimit = 3 //bulk 错误重试机制
	` + op.Name + ` = "` + index + `"
   )


type ` + op.Name + `Es struct {
	index   string
	mapping map[string]interface{}
	client  *elastic.Client
}
	`
	//方法
	content += `
func New` + op.Name + `Es(client *elastic.Client) *` + op.Name + `Es {
	index := ` + op.Name + `
	` + op.Name + `Es := &` + op.Name + `Es{
		client:  client,
		index:   index,
		mapping: model.GetMapping(),
	}

	` + op.Name + `Es.init()

	return ` + op.Name + `Es
}

func (es *` + op.Name + `Es) init() {
	ctx := context.Background()
	exists, err := es.client.IndexExists(es.index).Do(ctx)
	if err != nil {
		fmt.Printf("` + op.Name + `Es init exist failed err is %s\n", err)
		return
	}

	if !exists {
		_, err := es.client.CreateIndex(es.index).BodyJson(es.mapping).Do(ctx)
		if err != nil {
			fmt.Printf("` + op.Name + `Es init failed err is %s\n", err)
			return
		}
	}
}



func (es *` + op.Name + `Es) BatchAdd(ctx context.Context, user []*model.` + op.Name + `) error {
	var err error
	for i := 0; i < esRetryLimit; i++ {
		if err = es.Create(ctx, user); err != nil {
			fmt.Println("` + op.Name + ` add failed ", err)
			continue
		}
		return err
	}
	return err
}

func (es *` + op.Name + `Es) Create(ctx context.Context, user []*model.` + op.Name + `) error {
	req := es.client.Bulk().Index(es.index)
	fmt.Println(user)
	for _, u := range user {
		//u.UpdateTime = time.Now().Format("2006-01-02 15:04:05")
		//u.CreateTime = time.Now().Format("2006-01-02 15:04:05")
		doc := elastic.NewBulkIndexRequest().Id(strconv.FormatUint(uint64(u.Id), 10)).Doc(u)
		req.Add(doc)
	}
	if req.NumberOfActions() < 0 {
		return nil
	}
	res, err := req.Do(ctx)
	if err != nil {
		return err
	}
	// 所以如果没有出错，说明全部成功了，直接返回即可
	if !res.Errors {
		return nil
	}
	for _, it := range res.Failed() {
		if it.Error == nil {
			continue
		}
		return &elastic.Error{
			Status:  it.Status,
			Details: it.Error,
		}
	}
	return nil
}

func (es *` + op.Name + `Es) BatchUpdate(ctx context.Context, user []*model.` + op.Name + `) error {
	var err error
	for i := 0; i < esRetryLimit; i++ {
		if err = es.Edit(ctx, user); err != nil {
			continue
		}
		return err
	}
	return err
}

func (es *` + op.Name + `Es) Edit(ctx context.Context, user []*model.` + op.Name + `) error {
	req := es.client.Bulk().Index(es.index)
	for _, u := range user {
		//u.UpdateTime = time.Now().Format("2006-01-02 15:04:05")
		doc := elastic.NewBulkUpdateRequest().Id(strconv.FormatUint(uint64(u.Id), 10)).Doc(u)
		req.Add(doc)
	}

	if req.NumberOfActions() < 0 {
		return nil
	}
	res, err := req.Do(ctx)
	if err != nil {
		return err
	}
	// 所以如果没有出错，说明全部成功了，直接返回即可
	if !res.Errors {
		return nil
	}
	for _, it := range res.Failed() {
		if it.Error == nil {
			continue
		}
		return &elastic.Error{
			Status:  it.Status,
			Details: it.Error,
		}
	}

	return nil
}

func (es *` + op.Name + `Es) BatchDel(ctx context.Context, user []*model.` + op.Name + `) error {
	var err error
	for i := 0; i < esRetryLimit; i++ {
		if err = es.Delete(ctx, user); err != nil {
			continue
		}
		return err
	}
	return err
}

func (es *` + op.Name + `Es) Delete(ctx context.Context, user []*model.` + op.Name + `) error {
	req := es.client.Bulk().Index(es.index)
	for _, u := range user {
		doc := elastic.NewBulkDeleteRequest().Id(strconv.FormatUint(uint64(u.Id), 10))
		req.Add(doc)
	}

	if req.NumberOfActions() < 0 {
		return nil
	}

	res, err := req.Do(ctx)
	if err != nil {
		return err
	}
	// 所以如果没有出错，说明全部成功了，直接返回即可
	if !res.Errors {
		return nil
	}
	for _, it := range res.Failed() {
		if it.Error == nil {
			continue
		}
		return &elastic.Error{
			Status:  it.Status,
			Details: it.Error,
		}
	}

	return nil
}

// MGet 根据id 批量获取
func (es *` + op.Name + `Es) GetEs(ctx context.Context, IDS []uint64) ([]*model.` + op.Name + `, error) {
	` + op.Name + ` := make([]*model.` + op.Name + `, 0, len(IDS))
	idStr := make([]string, 0, len(IDS))
	for _, id := range IDS {
		idStr = append(idStr, strconv.FormatUint(id, 10))
	}
	resp, err := es.client.Search(es.index).Query(
		elastic.NewIdsQuery().Ids(idStr...)).Size(len(IDS)).Do(ctx)

	if err != nil {
		return nil, err
	}

	if resp.TotalHits() == 0 {
		return nil, nil
	}
	for _, e := range resp.Each(reflect.TypeOf(&model.` + op.Name + `{})) {
		us := e.(*model.` + op.Name + `)
		` + op.Name + ` = append(` + op.Name + `, us)
	}
	return ` + op.Name + `, nil
}

func (es *` + op.Name + `Es) Search(ctx context.Context, filter *client.EsSearch) ([]*model.` + op.Name + `, error) {
	boolQuery := elastic.NewBoolQuery()
	boolQuery.Must(filter.MustQuery...)
	boolQuery.MustNot(filter.MustNotQuery...)
	boolQuery.Should(filter.ShouldQuery...)
	boolQuery.Filter(filter.Filters...)

	// 当should不为空时，保证至少匹配should中的一项
	if len(filter.MustQuery) == 0 && len(filter.MustNotQuery) == 0 && len(filter.ShouldQuery) > 0 {
		boolQuery.MinimumShouldMatch("1")
	}
	fmt.Println("这是查询参数", boolQuery)
	service := es.client.Search().Index(es.index).Query(boolQuery).SortBy(filter.Sorters...).From(filter.From).Size(filter.Size)
	resp, err := service.Do(ctx)
	if err != nil {
		return nil, err
	}
	if resp.TotalHits() == 0 {
		return nil, nil
	}
	` + op.Name + ` := make([]*model.` + op.Name + `, 0)
	for _, e := range resp.Each(reflect.TypeOf(&model.` + op.Name + `{})) {
		us := e.(*model.` + op.Name + `)
		` + op.Name + ` = append(` + op.Name + `, us)
	}
	return ` + op.Name + `, nil
}

// AggsSiteVisitLog 聚合统计站点访问量, 得到每月的总pv,uv
func (es *` + op.Name + `Es) Agg(ctx context.Context, filter *client.EsSearch) (bs []byte, err error) {

	boolQuery := elastic.NewBoolQuery()
	boolQuery.Must(filter.MustQuery...)
	boolQuery.MustNot(filter.MustNotQuery...)
	boolQuery.Should(filter.ShouldQuery...)
	boolQuery.Filter(filter.Filters...)

	// 当should不为空时，保证至少匹配should中的一项
	if len(filter.MustQuery) == 0 && len(filter.MustNotQuery) == 0 && len(filter.ShouldQuery) > 0 {
		boolQuery.MinimumShouldMatch("1")
	}
	
	service := es.client.Search().
		Index(es.index).Query(boolQuery).Size(filter.Size).From(filter.From)
	result, err := service.Aggregation("a", filter.Aggregation).Do(ctx)
	if err != nil {
		return
	}
	// 结构体有点复杂, 使用log先打印出结果再写结构体解析吧
	
	bs, err = result.Aggregations["a"].MarshalJSON()
	if err != nil {
		// 没有查询到terms聚合结果
		fmt.Println("we should have a terms aggregation called %q", "timeline")
		return nil, err
	}
	return
}
    `
	filename := ""
	if op.Path == "" {
		filename = conf.EsDaoPath + op.Name + ".go"
	} else {
		filename = op.Path + op.Name + ".go"
	}
	tools.Write(filename, op.Name, content, conf.EsDaoPath)
}
